#include	"Asgn3_code/hw_addrs.h"
#include 	"config.h"
#include	"unp.h"
#include	<linux/if_ether.h>
#include	<linux/if_packet.h>
#include	<linux/if_arp.h>

#define TRUE	1
#define FALSE	0

static int ephemeralPort = 7000;

char* getMyIP() {

	struct hwa_info	*hwa, *hwahead;
	struct sockaddr_in	*sa;
	char   *ptr;
	char *hostname;
	hostname = (char*)malloc(INET_ADDRSTRLEN);

	for(hwahead = hwa = Get_hw_addrs(); hwa != NULL; hwa = hwa->hwa_next) {
			
		if(strcmp(hwa->if_name,"eth0\0") == 0)
		{
			if((sa = (struct sockaddr_in*)hwa->ip_addr) != NULL) {
				Inet_ntop(AF_INET, &sa->sin_addr, hostname, INET_ADDRSTRLEN);
			return hostname;
		}

		}
		
	}
	//Exit - No IP found.
	printf("\nError: cannot get local IP\n");
	fflush(stdout);
	exit(0);	
}

char* getMyHostDomain() {

	struct hwa_info	*hwa, *hwahead;
	struct sockaddr_in	*sa;
	char   *ptr;
	int    i, prflag;
	struct hostent *host;
	char *hostname;

	for (hwahead = hwa = Get_hw_addrs(); hwa != NULL; hwa = hwa->hwa_next) {
			
		if(strcmp(hwa->if_name,"eth0\0") == 0 )
		{
			if ( (sa = (struct sockaddr_in*)hwa->ip_addr) != NULL)
					
			host = gethostbyaddr((char *)(&(sa->sin_addr)), sizeof(sa->sin_addr), AF_INET);
			if(host == NULL)
				return NULL;

			
			hostname = (char *)malloc(strlen(host->h_name));
			hostname = strncpy(hostname,host->h_name,strlen(host->h_name));	
			hostname[strlen(host->h_name)] = '\0';
			return hostname;
		}
	}
	
	return NULL;

}

/**
	 Add a node to the demux list.
*/

void add_node_demux(struct demuxTableNode *node, struct demuxTableNode **start) {
	
	if(*start == NULL)
		*start = node;
	else
	{
		struct demuxTableNode *temp;
		temp = *start;
		
		while(temp!=NULL) {
			
			if(temp->next != NULL)
				temp = temp->next;
			else {
			        temp->next = node;
				break;
			}
		}
	}
}


void add_purge_demux_table(struct sockaddr_un *peer, struct demuxTableNode **start,struct demuxTableNode **node) {
	
	struct demuxTableNode *temp, *temp2;
	struct demuxTableNode *entry = NULL;
	
	int found = 0;

	temp = *start;
	
	while(temp != NULL) {
		
		if(temp->isPermanent) {
			if(temp->next == NULL) {
				break;
			}
			temp = temp->next;
			continue;
	}

	if(strcmp(temp->sun_path,peer->sun_path) == 0) {

				temp->timestamp = time(NULL);
				*node = temp;
				found = 1;
				break;
		}
		if(temp->next == NULL)
			break;

		temp = temp->next;
	}

	if(temp!=NULL && entry == NULL && found == 0) {
		entry = (struct demuxTableNode*)malloc(sizeof(struct demuxTableNode));

		strncpy(entry->sun_path,peer->sun_path,strlen(peer->sun_path)+1);
		entry->port_num = ephemeralPort;
		ephemeralPort++;
		entry->isPermanent = false;
		entry->timestamp = time(NULL);
		entry->next = NULL;
		temp->next = entry;
	}
	
	/* purge old entries */
	
	temp = *start;
	temp2 = temp;

	while(temp->next!=NULL) {		/* 1st entry is permanent */

		if(temp->isPermanent) {
			temp = temp->next;
			continue;
		}
		
		if((time(NULL) - temp->timestamp) > DEMUX_STALE_TIME) {

			temp = temp->next;
			temp2->next = temp;
		}
		else {

			temp2 = temp; 
			temp = temp->next;
		}
	}	
	
	if(entry != NULL)
		*node = entry;	

}

struct routingTableNode* findRouteEntry(char *cip, int staleness,struct routingTableNode **start) {

	struct routingTableNode *temp,*temp2;
	temp = *start;
	temp2 = *start;

	if(temp == NULL) {

		return NULL;
	}
	
	while(temp != NULL) {
		
		if(strcmp(temp->cip,cip) == 0 && (time(NULL) - temp->timestamp)  > staleness) {

			struct routingTableNode *entry;

			if(*start == temp2)
				*start = temp2->next;
			else 
				temp2->next = temp->next;	
			
			if(staleness == 0) {
				entry = (struct routingTableNode*)malloc(sizeof(struct routingTableNode));
				memcpy(entry, temp, sizeof(struct routingTableNode));
				free(temp);
				return entry;
			}
			else { 
				free(temp);
				return NULL;
			}
		}
		else if(strcmp(temp->cip,cip) == 0 && (time(NULL) - temp->timestamp)  <= staleness) {
			return temp;
		}

		temp2 = temp;
		temp = temp->next;
		
	}

	return NULL;
}

void add_purge_data(struct msgSendBuff *msg,int broadcast, char *ip, struct dataBuff **head, int port) {
	
	struct dataBuff *temp, *next;
	temp = *head;
	
	struct dataBuff *node = (struct dataBuff*)malloc(sizeof(struct dataBuff));
	node->broadcast = broadcast;
	memcpy((void*)&(node->msg),(void*) msg, sizeof(struct msgSendBuff));
	strncpy(node->ip,ip,INET_ADDRSTRLEN);
	node->timestamp = time(NULL);
	node->sourcePort = port;
	node->next = NULL;
	
	if(*head == NULL) {

		*head = node;
	}
	else {
		while(temp!=NULL) {
			if(temp->next != NULL)
				temp = temp->next;	
			else {
				temp->next = node;
				break;
			}
		}
	}

	temp = *head;
	
	if(temp->next == NULL) {
		if((time(NULL) - temp->timestamp) > DATA_STALE_TIME) {
			temp = temp->next;
			free(*head);
			*head = temp;
			return;
		}
		
	}
	
	while(temp != NULL) {
		
		if((time(NULL) - temp->timestamp) > DATA_STALE_TIME) {
			next = temp->next;
			free(temp);
			temp = next;
		}
		else
			temp = temp->next;
	}
}

void add_route(void * buffer, int interface, struct routingTableNode **head) {

	struct routingTableNode *temp = *head;
	struct routingTableNode *temp2;
	int flag = 0;

	temp2 = (struct routingTableNode *)malloc(sizeof(struct routingTableNode));

	if(temp != NULL) {

		while(temp->next!=NULL) 
			temp = temp->next;
	
	}
	else
		flag = TRUE;

	struct ethhdr *eth;
	eth  = (struct ethhdr*)buffer;

	int *type2 = (int*)(buffer+14);
	int type = ntohs(*type2);
			
	if(type == RREQ_TYPE) {
		
		struct RREQPacket *rreq = (struct RREQPacket*)(buffer+14);
		strncpy(temp2->cip,rreq->sourceIP,INET_ADDRSTRLEN);
		temp2->hopCount = ntohs(rreq->hopCount);
		temp2->timestamp = time(NULL);
		temp2->outInterface = interface;
		memcpy(temp2->nextHop, (char*)eth->h_source, HW_ADDR);
		temp2->next = NULL;	

		printf("\n\n NEXT HOP: %s %x \n", temp2->nextHop, (char*)eth->h_source);
		char *ptr;
		ptr = (char*)eth->h_source;
		int j = HW_ADDR;
		
		do {

			printf("%.2x%s", *ptr++ & 0xff, (j==1) ? " " : ":");

		   } while( --j > 0);


				
		fflush(stdout);
	} 	

	if(type == RREP_TYPE) {

		struct RREPPacket *rrep = (struct RREPPacket*)(buffer+14);
		
		strncpy(temp2->cip,rrep->destIP,INET_ADDRSTRLEN);
		temp2->hopCount = ntohs(rrep->hopCount);
		temp2->timestamp = time(NULL);
		temp2->outInterface = interface;
	   	memcpy(temp2->nextHop, (char *)eth->h_source, HW_ADDR);	
		temp2->next = NULL;		
		printf("\n\n NEXT HOP: %s %x \n", temp2->nextHop, (char*)eth->h_source);
		char *ptr;
		ptr = (char*)eth->h_source;
		int j = HW_ADDR;
		
		do {

			printf("%.2x%s", *ptr++ & 0xff, (j==1) ? " " : ":");

		   } while( --j > 0);


				
		fflush(stdout);
	

	}

	if(flag) 
		*head = temp2;	
	else
		temp->next = temp2;
}

void deleteRoute(struct routingTableNode *node, struct routingTableNode **head) {
		
	struct routingTableNode *temp = *head;
	struct routingTableNode *temp2;
	/* Routing table has at least 1 entry */
	
	if(temp->next == NULL) {
		if(strcmp(temp->cip,node->cip) == 0) {

			*head = temp->next;
			free(temp);
			return;
		}
	}
	
	if(strcmp(temp->cip,node->cip) == 0) {
			
						
			*head = temp->next;
			free(temp);
			return;
	}

	temp2 = temp;
	temp = temp->next;

	while(temp != NULL) {
		
		if(strcmp(temp->cip,node->cip) == 0) {
			
			temp2->next = temp->next;
			free(temp);
			return;
		}

		temp2 = temp2->next;
		temp = temp->next;	
	}
	
}

int findPort(struct demuxTableNode **head, char* sunPath){

	struct demuxTableNode *temp = *head;	

	while(temp!=NULL){

		if(strcmp(temp->sun_path, sunPath)==0) {
						
			return(temp->port_num);
		}	

		temp = temp->next;
	}
	
	return -1;
}

void updateTimeStampDemux(struct demuxTableNode **head, char* sunPath){

	struct demuxTableNode *temp = *head;	

	while(temp!=NULL){

		if(strcmp(temp->sun_path, sunPath)==0) {
						
			temp->timestamp = time(NULL);
		}	

		temp = temp->next;
	}
	
}

char* find_sock_path(struct demuxTableNode **head, int port){

	struct demuxTableNode *temp = *head;	
	char *sun_path;
	sun_path = (char *)malloc(MAXLINE*sizeof(char));

	while(temp!=NULL){

		if(temp->port_num == port) {
						
			strncpy(sun_path, temp->sun_path, strlen(temp->sun_path)+1);
			return sun_path;
		}

		temp = temp->next;
	}
}

struct dataBuff* findDataInBuffer( char *sourceIP, int broadcastID, struct dataBuff **head) {

	struct dataBuff *temp = *head;
	struct dataBuff *temp2, *temp3;

	if(temp == NULL)
		return NULL;
	
	temp2 = temp;
	if((strcmp(temp2->ip, sourceIP) == 0) && temp2->broadcast == broadcastID) {
		*head = temp2->next;
		return temp2;
	}
	
	temp = temp->next;

	while(temp != NULL) {

		if((strcmp(temp->ip, sourceIP) == 0) && temp->broadcast == broadcastID) {
			temp2->next = temp->next;
			return temp;
		}	
			temp2 = temp;
			temp = temp->next;
	}

	return NULL;
}

char* getHostName(char* sourceIP) {


	char* hostName;
	struct hostent *addrptr;
	struct in_addr ip;
	
		
	inet_pton(AF_INET, sourceIP, &ip);
	
	addrptr = gethostbyaddr(&ip, sizeof(struct in_addr), AF_INET);
	hostName = (char*)malloc(strlen(addrptr->h_name));
	strncpy(hostName, addrptr->h_name, strlen(addrptr->h_name)+1);
	
	return(hostName);
}

void printRoute(struct routingTableNode **head) {

	struct routingTableNode *temp;
	temp = *head;
	
	while(temp != NULL ) {
		printf("Next hop: %x\n ", temp->nextHop);
		printf("hopCount : %d\n", temp->hopCount);
		printf("timestamp : %d\n", temp->timestamp);
		printf("IP: 	%s\n", temp->cip);
		fflush(stdout);
		
		temp = temp->next;
	}

}

void printDemux(struct demuxTableNode **head) {

	struct demuxTableNode *temp;
	temp = *head;
	
	while(temp != NULL ) {
		printf("sun_path : %s\n", temp->sun_path);
		printf("Port: 	%d\n", temp->port_num);
		fflush(stdout);
		
		temp = temp->next;
	}

}

void add_unique_rreq(struct RREQRx **head, int broadcast, char* ip) {

	struct RREQRx *temp, *entry;
	temp = *head;
	
	entry = (struct RREQRx*)malloc(sizeof (struct RREQRx));
	entry->broadcast = broadcast;
	strncpy(entry->sourceIP, ip, INET_ADDRSTRLEN);
	entry->next = NULL;
 
	if(temp == NULL) {
		*head = entry;
		return;				
	}
	
	while(temp->next != NULL) {
		
		temp = temp->next;
	}	

	temp->next = entry;
	
}

int isReceived(struct RREQRx **head, int broadcast, char* ip) {

	struct RREQRx *temp;
	temp = *head;

	while(temp != NULL) {
		if((temp->broadcast == broadcast) && strcmp(temp->sourceIP, ip) == 0)
			return 1;

		temp = temp->next;
	}

	return -1;
}
	
void updateTimeStampRoute(struct routingTableNode **node) {

	struct routingTableNode *temp = *node;	

	if(temp != NULL)
	{
		temp->timestamp = time(NULL);
	}

}

